package com.rsscollector.util;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.log4j.Logger;
import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

import java.io.*;
import java.net.URL;
import java.util.Vector;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * IO.java provides some IO utilities
 */
public class IO {

	private static Logger log = Logger.getLogger(IO.class);
	private static final int BUFFER_SIZE = 2048;

	public static void createFile(String sFile) throws IOException {
		File f = new File(sFile);
		if (!f.exists()) {
			f.createNewFile();
		}
	}

	/**
	 * Append data to file
	 *
	 * @param sFileName
	 * @param sLine
	 * @throws IOException
	 */
	public static void appendString(String sFileName, String sLine)
			throws IOException {
		FileWriter fwr = null;
		try {
			createFile(sFileName);
			fwr = new FileWriter(sFileName, true);
			fwr.write("\n" + sLine);
		} catch (IOException ex) {
			throw ex;
		} finally {
			try {
				fwr.close();
			} catch (Exception ex) {
			}
		}
	}

	/**
	 * Close stream
	 *
	 * @param stm
	 */
	public static void close(OutputStream stm) {
		if (stm != null) {
			try {
				stm.close();
			} catch (IOException ex) {
				log.error("Failed to close output stream !!!", ex);
			}
		}
	}

	/**
	 * Close stream
	 *
	 * @param stm
	 */
	public static void close(InputStream stm) {
		if (stm != null) {
			try {
				stm.close();
			} catch (IOException ex) {
				log.error("Failed to close input stream !!!", ex);
			}
		}
	}

	/**
	 * Open input stream from a string
	 *
	 * @param str
	 * @return InputStream
	 */
	public static InputStream openInputStream(final String str) {
		return new ByteArrayInputStream(str.getBytes());
	}

	/**
	 * read the content of a file
	 *
	 * @param url
	 * @return
	 * @throws IOException
	 */
	public static byte[] readFile(URL url) throws IOException {
		InputStream fis = null;
		BufferedInputStream bis = null;
		ByteArrayOutputStream container = new ByteArrayOutputStream();
		byte[] buff = new byte[BUFFER_SIZE];

		log.debug("IO.readFile(" + url + ")");

		try {
			fis = new DataInputStream(url.openConnection().getInputStream());
			bis = new BufferedInputStream(fis);
			int nByteRead = -1;
			while ((nByteRead = bis.read(buff, 0, BUFFER_SIZE)) != -1) {
				container.write(buff, 0, nByteRead);
			}
			container.flush();
			return container.toByteArray();
		} catch (IOException ex) {
			throw ex;
		} finally {
			IO.close(bis);
			IO.close(fis);
			IO.close(container);
		}
	}

	/**
	 * read the content of a file
	 *
	 * @param sFileName
	 * @return
	 * @throws IOException
	 */
	public static byte[] readFile(final String sFileName) throws IOException {
		FileInputStream fis = null;
		BufferedInputStream bis = null;
		ByteArrayOutputStream container = new ByteArrayOutputStream();
		byte[] buff = new byte[BUFFER_SIZE];
		try {
			fis = new FileInputStream(sFileName);
			bis = new BufferedInputStream(fis);
			int nByteRead = -1;
			while ((nByteRead = bis.read(buff, 0, BUFFER_SIZE)) != -1) {
				container.write(buff, 0, nByteRead);
			}
			container.flush();
			return container.toByteArray();
		} catch (IOException ex) {
			throw ex;
		} finally {
			IO.close(bis);
			IO.close(fis);
			IO.close(container);
		}
	}

	/**
	 * read data from URL
	 *
	 * @param url
	 * @return
	 * @throws IOException
	 */
	public static byte[] readURL(URL url) throws IOException {
		InputStream fis = null;
		BufferedInputStream bis = null;
		ByteArrayOutputStream container = new ByteArrayOutputStream();
		byte[] buff = new byte[BUFFER_SIZE];

		try {
			fis = new DataInputStream(url.openStream());
			bis = new BufferedInputStream(fis);
			int nByteRead = -1;
			while ((nByteRead = bis.read(buff, 0, BUFFER_SIZE)) != -1) {
				container.write(buff, 0, nByteRead);
			}
			container.flush();
			return container.toByteArray();
		} catch (IOException ex) {
			throw ex;
		} finally {
			bis.close();
			fis.close();
			container.close();
		}
	}

	/**
	 * Read version file
	 *
	 * @param sPath
	 * @return
	 * @throws IOException
	 */
	public static String readVersion(String sPath) throws IOException {


		String sLine = null;
		String sKeep = null;

		FileInputStream fis = null;
		DataInputStream din = null;

		try {
			fis = new FileInputStream(sPath);
			din = new DataInputStream(fis);

			while ((sLine = din.readLine()) != null) {

				int nEqualIndex = sLine.indexOf("=");

				if (nEqualIndex < 0) {
					continue;
				}

				sKeep = sLine;
				String sTemp = sLine.toLowerCase();

				int nVersionIndex = sTemp.indexOf("version");

				if (nVersionIndex < 0) {
					continue;
				}

				return sLine.substring(nEqualIndex + 1).trim();
			}
		} catch (IOException ex) {
			throw ex;
		} finally {
			IO.close(din);
			IO.close(fis);
		}


		if (sKeep != null) {
			int nEqualIndex = sKeep.indexOf("=");
			return sKeep.substring(nEqualIndex + 1).trim();
		}

		return "";
	}

	/**
	 * read the content of a file
	 *
	 * @param is
	 * @return
	 * @throws IOException
	 */
	public static byte[] readStream(InputStream is) throws IOException {
		BufferedInputStream bis = null;
		ByteArrayOutputStream container = new ByteArrayOutputStream();
		byte[] buff = new byte[BUFFER_SIZE];
		try {
			bis = new BufferedInputStream(is);
			int nByteRead = -1;
			while ((nByteRead = bis.read(buff, 0, BUFFER_SIZE)) != -1) {
				container.write(buff, 0, nByteRead);
			}
			container.flush();
			return container.toByteArray();
		} catch (IOException ex) {
			throw ex;
		} finally {
			IO.close(bis);
			IO.close(container);
		}
	}

	/**
	 * check if the file exists
	 *
	 * @param sFilePath
	 * @return boolean
	 */
	public static boolean exist(final String sFilePath) {
		File f = new File(sFilePath);
		return f.exists();
	}

	/**
	 * delete a file
	 *
	 * @param sPath
	 */
	public static void remove(final String sPath) {
		if (!IO.exist(sPath)) {
			return;
		}
		File f = new File(sPath);
		if (f.isFile()) {
			if (f.getName().equals(".") || f.getName().equals("..")) {
				return;
			}
			f.delete();
		} else {
			File[] list = f.listFiles();
			for (int i = 0; i < list.length; i++) {
				File one = list[i];
				IO.remove(one.getAbsolutePath());
			}
			f.delete();
		}
	}

	/**
	 * create a file with content
	 * on hard disk
	 *
	 * @param sFileName
	 * @param data
	 * @throws IOException
	 */
	public static void writeFile(final String sFileName, final byte[] data)
			throws IOException {
		FileOutputStream fos = null;
		BufferedOutputStream bos = null;
		try {
			fos = new FileOutputStream(sFileName);
			bos = new BufferedOutputStream(fos);
			int length = data.length;
			bos.write(data, 0, length);
			bos.flush();
		} catch (IOException ex) {
			throw ex;
		} finally {
			IO.close(bos);
			IO.close(fos);
		}
	}

	/**
	 * create a file and
	 * put it onto a folder
	 *
	 * @param sFilePath
	 * @param data
	 * @throws IOException
	 */
	public static void createFileWithFolder(final String sFilePath,
														 final byte[] data) throws IOException {
		String sDir = getFolder(sFilePath);
		IO.createDirs(sDir);
		try {
			IO.writeFile(sFilePath, data);
		} catch (IOException ex) {
			throw ex;
		}
	}

	/**
	 * create directories
	 *
	 * @param sDirPath
	 */
	public static void createDirs(final String sDirPath) {
		File file = new File(sDirPath);
		if (file.exists()) {
			return;
		}
		file.mkdirs();
	}

	/**
	 * convert an object to byte arrray
	 */
	public static byte[] objectToBytes(final Serializable obj)
			throws IOException {
		ByteArrayOutputStream byteStm = new ByteArrayOutputStream();
		ObjectOutputStream objStm = null;
		try {
			objStm = new ObjectOutputStream(byteStm);
			objStm.writeObject(obj);
			objStm.flush();
		} catch (IOException ex) {
			throw ex;
		} finally {
			IO.close(objStm);
			IO.close(byteStm);
		}
		byte[] objBytes = byteStm.toByteArray();
		return objBytes;
	}

	/**
	 * convert a byte array to an object
	 *
	 * @param bytes
	 * @return
	 * @throws IOException
	 * @throws ClassNotFoundException
	 */
	public static Object bytesToObject(final byte[] bytes) throws IOException,
			ClassNotFoundException {
		ByteArrayInputStream in = new ByteArrayInputStream(bytes);
		ObjectInputStream objIn = null;
		Object rs = null;
		try {
			objIn = new ObjectInputStream(in);
			rs = objIn.readObject();
		} catch (IOException ex) {
			throw ex;
		} catch (ClassNotFoundException ex) {
			throw ex;
		} finally {
			IO.close(objIn);
			IO.close(in);
		}
		return rs;
	}

	/**
	 * read content of a text file
	 *
	 * @param sFileName
	 * @return
	 * @throws IOException
	 */
	public static String readTextFile(final String sFileName)
			throws IOException {
		FileInputStream fis = null;
		DataInputStream din = null;
		String content = "";
		try {
			fis = new FileInputStream(sFileName);
			din = new DataInputStream(fis);
			String line = null;
			while ((line = din.readLine()) != null) {
				content += line + "\n";
			}
			return content;
		} catch (IOException ex) {
			throw ex;
		} finally {
			IO.close(din);
			IO.close(fis);
		}
	}

	/**
	 * read content of a text file
	 *
	 * @param sFileName
	 * @return
	 * @throws IOException
	 */
	public static Vector readEmailFile(final String sFileName)
			throws IOException {
		FileInputStream fis = null;
		DataInputStream din = null;
		Vector mailList = new Vector();
		try {
			fis = new FileInputStream(sFileName);
			din = new DataInputStream(fis);
			String line = null;
			while ((line = din.readLine()) != null) {
				mailList.add(line);
			}
			return mailList;
		} catch (IOException ex) {
			throw ex;
		} finally {
			IO.close(din);
			IO.close(fis);
			din = null;
			fis = null;
		}
	}

	/**
	 * Get folder path from
	 * a absolute file name
	 * @param sFileName file name
	 * @return folder path
	 */
	/*public static String getFolder(final String sFileName) {
				File file = new File(sFileName);
				String sParent = file.getParent();
				return sParent;
		  }*/

	/**
	 * write a text file on drive
	 *
	 * @param sFileName
	 * @param sContent
	 * @throws IOException
	 */
	public static void writeTextFile(String sFileName, String sContent) throws IOException {
		//use buffering
		File aFile = new File(sFileName);
		Writer output = new BufferedWriter(new FileWriter(aFile));
		try {
			//FileWriter always assumes default encoding is OK!
			output.write(sContent);
		}
		finally {
			output.close();
		}
	}

	/**
	 * add byte array to a zip file
	 *
	 * @param sName
	 * @param data
	 * @param zipOut
	 * @throws IOException
	 */
	public static void addByteToZip(final String sName, final byte[] data,
											  ZipOutputStream zipOut) throws IOException {
		ZipEntry entry = new ZipEntry(sName);
		try {
			zipOut.putNextEntry(entry);
			zipOut.write(data, 0, data.length);
			zipOut.flush();
		} catch (IOException ex) {
			throw ex;
		}
	}

	/**
	 * Read a file from an URL
	 *
	 * @param url
	 * @return a document object
	 * @throws JDOMException IOException, Exception
	 */
	public static Document readFileFromURL(final URL url) throws JDOMException, IOException, Exception {
		SAXBuilder builder = null;
		Document document = null;
		try {
			log.info("START readFileFromURL:\t" + url.getPath());
			builder = new SAXBuilder(false);
			document = builder.build(url);
			log.info("END readFileFromURL");
		} catch (JDOMException jdex) {
			throw jdex;
		} catch (IOException ioe) {
			throw ioe;
		} catch (Exception ex) {
			throw ex;
		}
		return document;
	}

	/**
	 * Read a file from a path
	 *
	 * @param filePath
	 * @return a document object
	 * @throws JDOMException IOException Exception
	 */
	public static Document readFileFromPath(final String filePath) throws JDOMException, IOException, Exception {
		SAXBuilder builder = null;
		Document document = null;
		try {
			log.info("START readFileFromPath:\t" + filePath);
			builder = new SAXBuilder(false);
			document = builder.build(filePath);
			log.info("END readFileFromPath");
		} catch (JDOMException jdex) {
			throw jdex;
		} catch (IOException ioe) {
			throw ioe;
		} catch (Exception ex) {
			throw ex;
		}
		return document;
	}

	public static boolean isFile(String sPath) {
		File file = new File(sPath);
		return file.isFile();
	}

	public static byte[] zipFolder(final String sParent) throws IOException {

		ByteArrayOutputStream container = null;
		ZipOutputStream zipOut = null;

		try {

			container = new ByteArrayOutputStream();
			zipOut = new ZipOutputStream(container);

			File file = new File(sParent);
			String[] childs = file.list();

			if (childs == null || childs.length == 0) {
				throw new IOException("Folder is empty");
			}

			for (int i = 0; i < childs.length; i++) {
				addFileToZipRecursive(sParent, childs[i], zipOut);
			}

		} catch (IOException ex) {
			throw ex;
		} finally {
			IO.close(zipOut);
			IO.close(container);
		}
		return container.toByteArray();
	}

	private static void addFileToZipRecursive(String sParent, String sFileName,
															ZipOutputStream zipOut) throws IOException {

		byte data[] = new byte[BUFFER_SIZE];
		BufferedInputStream bis = null;
		FileInputStream is = null;
		String sCurrent = sParent + "/" + sFileName;

		if (IO.isFile(sCurrent)) {

			try {

				is = new FileInputStream(sCurrent);
				bis = new BufferedInputStream(is, BUFFER_SIZE);
				ZipEntry entry = new ZipEntry(sFileName);
				zipOut.putNextEntry(entry);
				int count = -1;

				while ((count = bis.read(data, 0, BUFFER_SIZE)) != -1) {
					zipOut.write(data, 0, count);
				}

				zipOut.flush();

			} catch (IOException ex) {
				throw ex;
			} finally {
				IO.close(bis);
				IO.close(is);
			}

		} else {

			File file = new File(sCurrent);
			String[] childs = file.list();
			try {
				for (int i = 0; i < childs.length; i++) {
					addFileToZipRecursive(sParent, sFileName + "/" + childs[i],
							zipOut);
				}
			} catch (IOException ex) {
				throw ex;
			}
		}
	}

	/**
	 * Unzip one file
	 *
	 * @param bZipData
	 * @param sExtension
	 * @return
	 * @throws IOException
	 */
	public static byte[] unzipFirstFileWithExtension(final byte[] bZipData,
																	 final String sExtension) throws IOException {

		ZipInputStream zis = null;
		BufferedInputStream bis = null;
		ByteArrayInputStream bais = null;

		try {
			bais = new ByteArrayInputStream(bZipData);
			bis = new BufferedInputStream(bais);
			zis = new ZipInputStream(bis);
			ZipEntry entry = null;

			while ((entry = zis.getNextEntry()) != null) {
				final String sCurrentName = entry.getName();

				// filter and get first file with sExt extension
				if (!(sCurrentName == null || sCurrentName.trim().length() == 0)) {
					String sTemp = sCurrentName.trim().toLowerCase();
					if (sTemp.endsWith("." + sExtension)) {
						return readStream(zis);
					}
				}
			}
		} catch (IOException ex) {
			throw ex;
		} finally {
			IO.close(zis);
			IO.close(bis);
			IO.close(bais);
		}
		return null;
	}

	/**
	 * Get the file name from a absolutely file path
	 *
	 * @param sPath
	 * @return String
	 */
	public static String getFolder(final String sPath) {
		int index1 = sPath.lastIndexOf('/');
		int index2 = sPath.lastIndexOf('\\');
		int index = 0;
		if (index1 < index2) {
			index = index2;
		} else if (index1 > 0) {
			index = index1;
		}

		if (index > 0) {
			return sPath.substring(0, index);
		} else {
			return sPath.substring(0);
		}
	}

	public static String getContentFromURL(String url) {
		HttpClient client = new HttpClient();

		PostMethod httppost = new PostMethod(url);
		httppost.setRequestHeader("Content-Type", "text/plain; charset=UTF-8");
		;
		try {
			client.executeMethod(httppost);

			if (httppost.getStatusCode() == HttpStatus.SC_OK) {
				return httppost.getResponseBodyAsString();
			} else {
				log.warn("Unexpected failure: " + url + " with error status" + httppost.getStatusLine().toString());
				System.out.println("Unexpected failure: " + url + " with error status" + httppost.getStatusLine().toString());
			}
		} catch (IOException e) {
			e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
		} finally {
			httppost.releaseConnection();
		}
		return null;
	}
}


